package pers.lt.tools;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
 * create by liting
 * create time :2013-11-1
 * 复制BEAN，把非空的数据复制到另一个BEAN
 * 
 * ***/

/**
 * <p>
 * Title: BeanUtil
 * </p>
 * <p>
 * Description:
 * </p>
 */
public class BeanUtil extends org.springframework.beans.BeanUtils {

    public BeanUtil() {
        super();
    }

    /**
     * 复制某个对象为目标对象类型的对象
     *
     * @param <T>      目标对象类型参数
     * @param source   源对象
     * @param destType 目标对象类型
     * @return
     */
    public static <T> T copyAs(Object source, Class<T> destType) {
        if (source == null) {
            return null;
        }
        try {
            T dest = destType.newInstance();
            copyProperties(source, dest);
            return dest;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 复制源对象集合到目标对象列表
     *
     * @param <T>
     * @param source
     * @param destType
     * @return
     */
    public static <T> List<T> copyAs(Collection<?> source, Class<T> destType) {
        if (source == null) {
            return null;
        }
        ArrayList<T> result = new ArrayList<T>();
        for (Object object : source) {
            result.add(copyAs(object, destType));
        }
        return result;
    }

    /**
     * 拷贝时，忽略属性
     */
    public static final String[] IGNORE_PROPS = new String[]{"", ""};

    /**
     * @param source
     * @param target
     * @param ignore 是否忽略BeanUtils.IGNORE_PROPS的字段
     * @throws BeansException
     * @author WUCHEN 拷贝属性
     */
    public static void copyProperties(Object source, Object target, boolean ignore) throws BeansException {
        if (ignore) {
            copyProperties(source, target, IGNORE_PROPS);
        } else {
            copyProperties(source, target);
        }
    }

    /**
     * 拷贝值时，默认不拷贝空值
     *
     * @param source
     * @param target
     * @throws BeansException
     */
    public static void copyProperties(Object source, Object target) throws BeansException {
        copyPropertiesIgnoreNull(source, target, true);
    }

    /**
     * @param source
     * @param target
     * @param ignore 是否忽略BeanUtils.IGNORE_PROPS的字段
     * @throws BeansException
     * @author WUCHEN 拷贝属性，忽略元数据空值复制
     */
    public static void copyPropertiesIgnoreNull(Object source, Object target, boolean ignore) throws BeansException {
        _copyProperties(source, target, ignore, true);
    }

    /**
     * @param source
     * @param target
     * @param properties 忽略properties的字段
     * @throws BeansException
     * @author WUCHEN 拷贝属性，忽略元数据空值复制
     */
    public static void copyPropertiesIgnore(Object source, Object target, String[] properties) throws BeansException {
        _copyProperties(source, target, false, true, properties);
    }

    /**
     * @param source
     * @param target
     * @param ignore     是否忽略属性IGNORE_PROPS复制
     * @param ignoreNull 是否忽略空值复制
     * @throws BeansException
     * @author WUCHEN
     */
    private static void _copyProperties(Object source, Object target, boolean ignore, boolean ignoreNull)
            throws BeansException {

        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        PropertyDescriptor[] targetPds = getPropertyDescriptors(target.getClass());
        List<String> ignoreList = ignore ? null : Arrays.asList(IGNORE_PROPS);

        for (int i = 0; i < targetPds.length; i++) {
            PropertyDescriptor targetPd = targetPds[i];
            if (targetPd.getWriteMethod() != null && (ignore || (!ignoreList.contains(targetPd.getName())))) {

                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method getter = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(getter.getDeclaringClass().getModifiers())) {
                            getter.setAccessible(true);
                        }
                        Object value = getter.invoke(source, new Object[0]);

                        if (ignoreNull && value == null)
                            continue;

                        Method setter = targetPd.getWriteMethod();
                        if (!Modifier.isPublic(setter.getDeclaringClass().getModifiers())) {
                            setter.setAccessible(true);
                        }
                        setter.invoke(target, new Object[]{value});
                    } catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }
    }

    /**
     * @param source
     * @param target
     * @param ignore            是否忽略属性IGNORE_PROPS复制
     * @param ignoreNull        是否忽略空值复制
     * @param ignoreProperities 忽略属性
     * @throws BeansException
     */
    private static void _copyProperties(Object source, Object target, boolean ignore, boolean ignoreNull, String[] ignoreProperities)
            throws BeansException {

        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        PropertyDescriptor[] targetPds = getPropertyDescriptors(target.getClass());

        List ignoreList = ignore ? new ArrayList<String>() : new ArrayList(Arrays.asList(IGNORE_PROPS));
        List<String> ignoreProperitiesList = ignore ? new ArrayList<String>() : new ArrayList(Arrays.asList(ignoreProperities));
        ignoreList.addAll(ignoreProperitiesList);


        for (int i = 0; i < targetPds.length; i++) {
            PropertyDescriptor targetPd = targetPds[i];
            if (targetPd.getWriteMethod() != null && (ignore || (!ignoreList.contains(targetPd.getName())))) {

                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method getter = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(getter.getDeclaringClass().getModifiers())) {
                            getter.setAccessible(true);
                        }
                        Object value = getter.invoke(source, new Object[0]);

                        if (ignoreNull && value == null)
                            continue;

                        Method setter = targetPd.getWriteMethod();
                        if (!Modifier.isPublic(setter.getDeclaringClass().getModifiers())) {
                            setter.setAccessible(true);
                        }
                        setter.invoke(target, new Object[]{value});
                    } catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }
    } 
}
